package cz.mff.cuni.dpp.liboptions;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Seznam chyb objevených při zpracováni vstupu.
 * <p>
 * Možné chyby:
 * <ul>
 * <li>Chybí povinný option - {@link #getMissingObligatory()}</li>
 * <li>Chybí hodnota u optinu, který ji vyžaduje - {@link #getMissingValue()}</li>
 * <li>Vícenásobně použitý option - {@link #getMultipleUsage()}</li>
 * <li>Hodnota byla uvedena ale má špatný typ - {@link #getBadValue()}</li>
 * <li>Hodnota byla uvedena ale není v rozsahu (ve výčtu) -
 * {@link #getOutOfRange()}</li>
 * <li>Byl použit neznámý parametr - {@link #getUnrecognizedOption()}</li>
 * <li>Špatné použití oddělovače obyčejných agrumnetů -
 * {@link #isBadAgrumentsDelimiterUsed()}</li>
 * </ul>
 * <p>
 * (Všechny vrácené kolekce jsou kopie, které je možné editovat bez side efektu)
 * 
 */
public class ParsingErrors {

	private final Set<Option<?>> missingObligatory = new HashSet<Option<?>>();
	private final Set<Option<?>> missingValue = new HashSet<Option<?>>();
	private final Set<Option<?>> multipleUsage = new HashSet<Option<?>>();
	private final Map<Option<?>, String> badValue = new HashMap<Option<?>, String>();
	private final Map<Option<?>, String> outOfRange = new HashMap<Option<?>, String>();
	private final List<String> unrecognizedOption = new LinkedList<String>();

	private boolean badAgrumentsDelimiterUsed = false;

	/**
	 * 
	 * @return množina optionů, které jsou poviné, ale nebyli použity (editace
	 *         množiny nemá side effekty)
	 */
	public Set<Option<?>> getMissingObligatory() {
		return new HashSet<Option<?>>(missingObligatory);
	}

	/**
	 * @return množina optionů, u kterých chybí hodnota (editace množiny nemá
	 *         side effekty)
	 */
	public Set<Option<?>> getMissingValue() {
		return new HashSet<Option<?>>(missingValue);
	}

	/**
	 * @return množina optionů, které byli použity na svtupu vícekrát (editace
	 *         množiny nemá side effekty)
	 */
	public Set<Option<?>> getMultipleUsage() {
		return new HashSet<Option<?>>(multipleUsage);
	}

	/**
	 * @return množina optionů, u kterých byla uvedena špatná hodnota (nečíselná
	 *         pro číslo) (editace množiny nemá side effekty) (vylučuje se s
	 *         {@link #getOutOfRange()})
	 */
	public Map<Option<?>, String> getBadValue() {
		return new HashMap<Option<?>, String>(badValue);
	}

	/**
	 * @return množina optionů, u kterých byla uvedena hodnota mimo povolený
	 *         rozsah (mimo číslený, nebo mimo výčet) (editace množiny nemá side
	 *         effekty) (vylučuje se s {@link #getBadValue()})
	 */
	public Map<Option<?>, String> getOutOfRange() {
		return new HashMap<Option<?>, String>(outOfRange);
	}

	/**
	 * @return seznam nerozpoznanách optionů (agrument, neoodělený oddělovačem
	 *         obzčejncýh agrumentů, který začíná prefixem pro krátky, nebo
	 *         dlouhý option)
	 */
	public List<String> getUnrecognizedOption() {
		return new LinkedList<String>(unrecognizedOption);
	}

	/**
	 * 
	 * @return počet detekovaných chyb, každý Option položka v chybové kolekci
	 *         je jedena chzba stejně jako špatné použití oddělovače
	 *         <em>obzčejných argumnetů</em>
	 * 
	 * 
	 * @see #getMissingObligatory()
	 * @see #getMissingValue()
	 * @see #getOutOfRange()
	 * @see #getBadValue()
	 * @see #getUnrecognizedOption()
	 */
	public int getErrorsCount() {
		int errorCount = missingObligatory.size() //
				+ missingValue.size() //
				+ badValue.size() //
				+ outOfRange.size() //
				+ unrecognizedOption.size() //
				+ multipleUsage.size();

		if (badAgrumentsDelimiterUsed) {
			errorCount++;
		}

		return errorCount;
	}

	/**
	 * @return <code>true</code> pokud je nejaka chyba, jinak <code>false</code>
	 */
	public boolean hasErrors() {
		return getErrorsCount() > 0;
	}

	/**
	 * Vrací true, pokud byl špatně použit oddělovač
	 * <em>obzčejných argumentů</em> ({@link Option#ARGUMENTS_DELIMITER}). To
	 * nastane pokud je oddělovač použit dvakrát.
	 * 
	 * @return <code>true</code> pokud byla zjikštěna chyba, jinak
	 *         <code>false</code>
	 */
	public boolean isBadAgrumentsDelimiterUsed() {
		return badAgrumentsDelimiterUsed;
	}

	void setBadAgrumentsDelimiterUsed(boolean badAgrumentsDelimiterUsed) {
		this.badAgrumentsDelimiterUsed = badAgrumentsDelimiterUsed;
	}

	void addOutOfRange(Option<?> option, String stringValue) {
		outOfRange.put(option, stringValue);
	}

	void addBadValue(Option<?> option, String stringValue) {
		badValue.put(option, stringValue);
	}

	void addUnrecognizedOption(String token) {
		unrecognizedOption.add(token);
	}

	void addMissingObligatory(Option<?> option) {
		missingObligatory.add(option);
	}

	void addMissingValue(Option<?> option) {
		missingValue.add(option);
	}

	void addMultipleUsage(Option<?> option) {
		multipleUsage.add(option);
	}

}
